<?php
/*
* Copyright 2017 Baidu, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* Http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

namespace BaiduBce\Services\Nat;

use BaiduBce\Auth\BceV1Signer;
use BaiduBce\BceBaseClient;
use BaiduBce\Http\BceHttpClient;
use BaiduBce\Http\HttpHeaders;
use BaiduBce\Http\HttpMethod;
use BaiduBce\Http\HttpContentTypes;
use BaiduBce\Services\Nat\model\Billing;

/**
 * This module provides a client class for NAT.
 */
class NatClient extends BceBaseClient
{

    private $signer;
    private $httpClient;
    private $prefix = '/v1';

    /**
     * The NatClient constructor.
     *
     * @param array $config The client configuration
     */
    function __construct(array $config)
    {
        parent::__construct($config, 'nat');
        $this->signer = new BceV1Signer();
        $this->httpClient = new BceHttpClient();
    }

    /**
     * Create an nat with the specified options.
     *
     * @param string $name
     *          name of nat
     *
     * @param string $vpcId
     *          id of vpc which nat created in
     *
     * @param string $spec
     *          specification of nat
     *
     * @param array $eips
     *          eips which will be bound to the nat. The optional parameter
     *
     * @param Billing $billing
     *          billing information. The optional parameter, default paymentTiming is Postpaid
     *
     * @param string $clientToken
     *          if the clientToken is not specified by the user, a random String
     *          generated by default algorithm will be used.
     *
     * @param array $options
     *          The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating EipClient instance.
     *
     * @return mixed
     */
    public function createNat($name, $vpcId, $spec, $eips = null, $billing = null, $clientToken = null, $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');
        $body = array();
        $body['name'] = $name;
        $body['vpcId'] = $vpcId;
        $body['spec'] = $spec;
        if (!empty($eips)) {
            $body['eips'] = $this->objectToArray($eips);
        }
        if (empty($billing)) {
            $billing = $this->generateDefaultBilling();
        }
        $body['billing'] = $this->objectToArray($billing);
        $params = array();
        if (empty($clientToken)) {
            $params['clientToken'] = $this->generateClientToken();
        } else {
            $params['clientToken'] = $clientToken;
        }

        return $this->sendRequest(
            HttpMethod::POST,
            array(
                'config' => $config,
                'params' => $params,
                'body' => json_encode($body),
            ),
            '/nat'
        );
    }

    /**
     * PurchaseReserved nat with fixed duration,only Prepaid nat can do this
     *
     * @param string $natId
     *          nat id to be renewed
     *
     * @param Billing $billing
     *          billing information. The optional parameter, default reservationLength is 1
     *
     * @param string $clientToken
     *          if the clientToken is not specified by the user, a random String
     *          generated by default algorithm will be used.
     *
     * @param array $options
     *          The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating EipClient instance.
     *
     * @return mixed
     */
    public function purchaseReservedNat($natId, $billing = null, $clientToken = null, $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');
        $body = array();
        if (empty($billing)) {
            $reservation = array();
            $reservation['reservationTimeUnit'] = 'Month';
            $reservation['reservationLength'] = 1;
            $billingBody = array();
            $billingBody['reservation'] = $reservation;
            $body['billing'] = $billingBody;
        } else {
            $reservation = array();
            $reservation['reservationTimeUnit'] = $billing->reservation->reservationTimeUnit;
            $reservation['reservationLength'] = $billing->reservation->reservationLength;
            $billingBody = array();
            $billingBody['reservation'] = $reservation;
            $body['billing'] = $billingBody;
        }
        $params = array();
        if (empty($clientToken)) {
            $clientToken = $this->generateClientToken();
        }
        $params['clientToken'] = $clientToken;
        $params['purchaseReserved'] = '';
        return $this->sendRequest(
            HttpMethod::PUT,
            array(
                'config' => $config,
                'params' => $params,
                'body' => json_encode($body),
            ),
            '/nat/' . $natId
        );
    }

    /**
     * bind eips to the nat
     *
     * @param array $eips
     *          eip addresses to be bound
     *
     * @param string $natId
     *          nat id to be bound
     *
     * @param string $clientToken
     *          if the clientToken is not specified by the user, a random String
     *          generated by default algorithm will be used.
     *
     * @param array $options
     *          The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating EipClient instance.
     *
     * @return mixed
     */
    public function bindNat($eips, $natId, $clientToken = null, $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');
        $body = array();
        $body['eips'] = $this->objectToArray($eips);
        $params = array();
        if (empty($clientToken)) {
            $clientToken = $this->generateClientToken();
        }
        $params['bind'] = '';
        $params['clientToken'] = $clientToken;

        return $this->sendRequest(
            HttpMethod::PUT,
            array(
                'config' => $config,
                'params' => $params,
                'body' => json_encode($body),
            ),
            '/nat/' . $natId
        );
    }

    /**
     * unbind the eips from a nat instance
     *
     * @param array $eips
     *          eip addresses to be unbound
     *
     * @param string $natId
     *          nat id to be unbound
     *
     * @param string $clientToken
     *          if the clientToken is not specified by the user, a random String
     *          generated by default algorithm will be used.
     *
     * @param array $options
     *          The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating EipClient instance.
     *
     * @return mixed
     */
    public function unbindNat($eips, $natId, $clientToken = null, $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');
        $body = array();
        $body['eips'] = $this->objectToArray($eips);
        $params = array();
        if (empty($clientToken)) {
            $clientToken = $this->generateClientToken();
        }
        $params['unbind'] = '';
        $params['clientToken'] = $clientToken;

        return $this->sendRequest(
            HttpMethod::PUT,
            array(
                'config' => $config,
                'params' => $params,
                'body' => json_encode($body),
            ),
            '/nat/' . $natId
        );
    }

    /**
     * rename a nat instance
     *
     * @param string $natId
     *          nat id to be renamed
     *
     * @param string $name
     *          name to be updated
     *
     * @param string $clientToken
     *          if the clientToken is not specified by the user, a random String
     *          generated by default algorithm will be used.
     *
     * @param array $options
     *          The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating EipClient instance.
     *
     * @return mixed
     */
    public function renameNat($natId, $name, $clientToken = null, $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');
        $body = array();
        $body['name'] = $name;
        $params = array();
        if (empty($clientToken)) {
            $clientToken = $this->generateClientToken();
        }
        $params['clientToken'] = $clientToken;

        return $this->sendRequest(
            HttpMethod::PUT,
            array(
                'config' => $config,
                'params' => $params,
                'body' => json_encode($body),
            ),
            '/nat/' . $natId
        );
    }

    /**
     * release the nat(delete operation)
     * Only the Postpaid instance or Prepaid which is expired can be released.
     *
     * @param string $natId
     *          id of nat instance to be released
     *
     * @param string $clientToken
     *          if the clientToken is not specified by the user, a random String
     *          generated by default algorithm will be used.
     *
     * @param array $options
     *          The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating EipClient instance.
     *
     * @return mixed
     */
    public function releaseNat($natId, $clientToken = null, $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');
        if (empty($clientToken)) {
            $clientToken = $this->generateClientToken();
        }
        $params = array();
        $params['clientToken'] = $clientToken;

        return $this->sendRequest(
            HttpMethod::DELETE,
            array(
                'config' => $config,
                'params' => $params,
            ),
            '/nat/' . $natId
        );
    }

    /**
     * get a list of nat owned by the authenticated user and specified conditions.
     *
     * @param string $vpcId
     *          id of vpc which nat is in
     *
     * @param string $natId
     *          id of nat instance. The optional parameter
     *
     * @param string $name
     *          name of nat instance. The optional parameter
     *
     * @param string $ip
     *          eip address which is bound to nat instance. The optional parameter
     *
     * @param string $marker
     *          The optional parameter marker specified in the original request to specify
     *          where in the results to begin listing.
     *
     * @param int $maxKeys
     *          The optional parameter to specifies the max number of list result to return.
     *          The default value is 1000.
     *
     * @param array $options
     *           The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating EipClient instance.
     *
     * @return mixed
     */
    public function listNats($vpcId, $natId = null, $name = null, $ip = null, $marker = null, $maxKeys = 1000,
                             $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');
        $params = array();
        $params['vpcId'] = $vpcId;
        if ($natId !== null) {
            $params['natId'] = $natId;
        }
        if ($name !== null) {
            $params['name'] = $name;
        }
        if ($ip !== null) {
            $params['ip'] = $ip;
        }
        if ($marker !== null) {
            $params['marker'] = $marker;
        }
        if ($maxKeys !== null) {
            $params['maxKeys'] = $maxKeys;
        }

        return $this->sendRequest(
            HttpMethod::GET,
            array(
                'config' => $config,
                'params' => $params,
            ),
            '/nat'
        );
    }

    /**
     * get a single nat instance by nat id.
     *
     * @param string $natId
     *          id of nat instance
     *
     * @param array $options
     *           The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating EipClient instance.
     *
     * @return mixed
     */
    public function detailNats($natId, $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');

        return $this->sendRequest(
            HttpMethod::GET,
            array(
                'config' => $config,
            ),
            '/nat/' . $natId
        );
    }

    /**
     * convert object to array
     *
     * @param object $obj
     *
     * @return array
     */
    function objectToArray($obj)
    {
        if (is_array($obj)) {
            return $obj;
        }
        $_arr = is_object($obj) ? get_object_vars($obj) : $obj;
        $arr = array();
        foreach ($_arr as $key => $value) {
            $value = (is_array($value)) || is_object($value) ? $this->objectToArray($value) : $value;
            $arr[$key] = $value;
        }
        return $arr;
    }

    /**
     * Create HttpClient and send request
     *
     * @param string $httpMethod
     *          The Http request method
     *
     * @param array $varArgs
     *          The extra arguments
     *
     * @param string $requestPath
     *          The Http request uri
     *
     * @return mixed The Http response and headers.
     */
    private function sendRequest($httpMethod, array $varArgs, $requestPath = '/')
    {
        $defaultArgs = array(
            'config' => array(),
            'body' => null,
            'headers' => array(),
            'params' => array(),
        );

        $args = array_merge($defaultArgs, $varArgs);
        if (empty($args['config'])) {
            $config = $this->config;
        } else {
            $config = array_merge(
                array(),
                $this->config,
                $args['config']
            );
        }
        if (!isset($args['headers'][HttpHeaders::CONTENT_TYPE])) {
            $args['headers'][HttpHeaders::CONTENT_TYPE] = HttpContentTypes::JSON;
        }
        $path = $this->prefix . $requestPath;
        $response = $this->httpClient->sendRequest(
            $config,
            $httpMethod,
            $path,
            $args['body'],
            $args['headers'],
            $args['params'],
            $this->signer
        );

        $result = $this->parseJsonResult($response['body']);

        return $result;
    }

    /**
     * The method to generate a default Billing which is Postpaid.
     *
     * @return Billing object with Postpaid PaymentTiming.
     */
    private function generateDefaultBilling()
    {
        return new Billing('Postpaid');
    }

    /**
     * The default method to generate the random String for clientToken if the optional parameter clientToken
     * is not specified by the user.
     *
     * The default algorithm is Mersenne Twister to generate a random UUID,
     * @return String An random String generated by Mersenne Twister.
     */
    public static function generateClientToken()
    {
        $uuid = md5(uniqid(mt_rand(), true));
        return $uuid;
    }

    /**
     * Bind eips in the specified dnat.
     *
     * @param string $natId
     *          The dnat id to be bound
     *
     * @param array $dnatEips
     *          The list of eip addresses to be bound
     *
     * @param string $clientToken
     *          If the clientToken is not specified by the user, a random string
     *          generated by default algorithm will be used
     *
     * @param array $options
     *          The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating NatClient instance
     *
     * @return mixed
     */
    public function bindDnatEip($natId, $dnatEips, $clientToken = null, $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');
        $body = array();
        $params = array();
        if(empty($natId)) {
            throw new \InvalidArgumentException(
                'The request $natId should not be empty.'
            );
        }
        if(empty($dnatEips)) {
            throw new \InvalidArgumentException(
                'The request $dnatEips should not be empty.'
            );
        }
        $body['dnatEips'] = $this->objectToArray($dnatEips);
        if (empty($clientToken)) {
            $clientToken = $this->generateClientToken();
        }
        $params['bind'] = '';
        $params['clientToken'] = $clientToken;
        return $this->sendRequest(
            HttpMethod::PUT,
            array(
                'config' => $config,
                'params' => $params,
                'body' => json_encode($body),
            ),
            '/nat/' . $natId
        );
    }

    /**
     * Unbind eips in the specified dnat.
     *
     * @param string $natId
     *          The dnat id to be bound
     *
     * @param array $dnatEips
     *          The list of eip addresses to be unbound
     *
     * @param string $clientToken
     *          If the clientToken is not specified by the user, a random string
     *          generated by default algorithm will be used
     *
     * @param array $options
     *          The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating NatClient instance
     *
     * @return mixed
     */
    public function unbindDnatEip($natId, $dnatEips, $clientToken = null, $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');
        $body = array();
        $params = array();
        if(empty($natId)) {
            throw new \InvalidArgumentException(
                'The request $natId should not be empty.'
            );
        }
        if(empty($dnatEips)) {
            throw new \InvalidArgumentException(
                'The request $dnatEips should not be empty.'
            );
        }
        $body['dnatEips'] = $this->objectToArray($dnatEips);
        if (empty($clientToken)) {
            $clientToken = $this->generateClientToken();
        }
        $params['unbind'] = '';
        $params['clientToken'] = $clientToken;
        return $this->sendRequest(
            HttpMethod::PUT,
            array(
                'config' => $config,
                'params' => $params,
                'body' => json_encode($body),
            ),
            '/nat/' . $natId
        );
    }

    /**
     * Create a snat rule in the specified snat.
     *
     * @param string $natId
     *          The nat id of the specified snat
     *
     * @param string $ruleName
     *          The name of the snat rule
     *
     * @param array $publicIpsAddress
     *          The list of the public ip addresses
     *
     * @param string $sourceCIDR
     *          The source cidr
     *
     * @param string $clientToken
     *          If the clientToken is not specified by the user, a random string
     *          generated by default algorithm will be used
     *
     * @param array $options
     *          The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating NatClient instance
     *
     * @return mixed
     */
    public function createSnatRule($natId, $ruleName, $publicIpsAddress, $sourceCIDR, $clientToken = null, $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');
        $body = array();
        $params = array();
        if(empty($natId)) {
            throw new \InvalidArgumentException(
                'The request $natId should not be empty.'
            );
        }
        if(empty($ruleName)) {
            throw new \InvalidArgumentException(
                'The request $ruleName should not be empty.'
            );
        }
        if(empty($publicIpsAddress)) {
            throw new \InvalidArgumentException(
                'The request $publicIpsAddress should not be empty.'
            );
        }
        if(empty($sourceCIDR)) {
            throw new \InvalidArgumentException(
                'The request $sourceCIDR should not be empty.'
            );
        }
        $body['publicIpsAddress'] = $this->objectToArray($publicIpsAddress);
        $body['sourceCIDR'] = $sourceCIDR;
        $body['ruleName'] = $ruleName;
        if (empty($clientToken)) {
            $clientToken = $this->generateClientToken();
        }
        $params['clientToken'] = $clientToken;
        return $this->sendRequest(
            HttpMethod::POST,
            array(
                'config' => $config,
                'params' => $params,
                'body' => json_encode($body),
            ),
            '/nat/' . $natId . '/snatRule'
        );
    }

    /**
     * Delete a snat rule via the snat rule id in the specified snat.
     *
     * @param string $natId
     *          The nat id of the specified snat
     *
     * @param string $ruleId
     *          The id of the snat rule to be deleted
     *
     * @param string $clientToken
     *          If the clientToken is not specified by the user, a random string
     *          generated by default algorithm will be used
     *
     * @param array $options
     *          The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating NatClient instance
     *
     * @return mixed
     */
    public function deleteSnatRule($natId, $ruleId, $clientToken = null, $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');
        $params = array();
        if(empty($natId)) {
            throw new \InvalidArgumentException(
                'The request $natId should not be empty.'
            );
        }
        if(empty($ruleId)) {
            throw new \InvalidArgumentException(
                'The request $ruleId should not be empty.'
            );
        }
        if (empty($clientToken)) {
            $clientToken = $this->generateClientToken();
        }
        $params['clientToken'] = $clientToken;
        return $this->sendRequest(
            HttpMethod::DELETE,
            array(
                'config' => $config,
                'params' => $params,
            ),
            '/nat/' . $natId . '/snatRule/' . $ruleId
        );
    }

    /**
     * Update a snat rule via the snat rule id in the specified snat.
     *
     * @param string $natId
     *          The nat id of the specified snat
     *
     * @param string $ruleId
     *          The id of the snat rule to be updated
     *
     * @param string $ruleName
     *          The name of the snat rule. The optional parameter
     *
     * @param string $sourceCIDR
     *          The source cidr. The optional parameter
     *
     * @param array $publicIpsAddress
     *          The list of the public ip addresses. The optional parameter
     *
     * @param string $clientToken
     *          If the clientToken is not specified by the user, a random string
     *          generated by default algorithm will be used
     *
     * @param array $options
     *          The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating NatClient instance
     *
     * @return mixed
     */
    public function updateSnatRule($natId, $ruleId, $ruleName = null, $sourceCIDR = null, $publicIpsAddress = null, $clientToken = null, $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');
        $body = array();
        $params = array();
        if(empty($natId)) {
            throw new \InvalidArgumentException(
                'The request $natId should not be empty.'
            );
        }
        if(empty($ruleId)) {
            throw new \InvalidArgumentException(
                'The request $ruleId should not be empty.'
            );
        }
        if (!empty($ruleName)) {
            $body['ruleName'] = $ruleName;
        }
        if (!empty($sourceCIDR)) {
            $body['sourceCIDR'] = $sourceCIDR;
        }
        if (!empty($publicIpsAddress)) {
            $body['publicIpsAddress'] = $this->objectToArray($publicIpsAddress);
        }
        if (empty($clientToken)) {
            $clientToken = $this->generateClientToken();
        }
        $params['clientToken'] = $clientToken;
        return $this->sendRequest(
            HttpMethod::PUT,
            array(
                'config' => $config,
                'params' => $params,
                'body' => json_encode($body),
            ),
            '/nat/' . $natId . '/snatRule/' . $ruleId
        );
    }

    /**
     * List the information of snat rules in one specified snat.
     *
     * @param string $natId
     *          The nat id of the specified snat
     *
     * @param string $marker
     *          The optional parameter marker specified in the original request to specify
     *          where in the results to begin listing.
     *
     * @param int $maxKeys
     *          The optional parameter to specifies the max number of list result to return.
     *          The default value is 1000.
     *
     * @param array $options
     *           The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating EipClient instance.
     *
     * @return mixed
     */
    public function listSnatRules($natId, $marker = null, $maxKeys = 1000, $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');
        $params = array();
        if(empty($natId)) {
            throw new \InvalidArgumentException(
                'The request $natId should not be empty.'
            );
        }
        if (!empty($marker)) {
            $params['marker'] = $marker;
        }
        if (!empty($maxKeys)) {
            $params['maxKeys'] = $maxKeys;
        }
        return $this->sendRequest(
            HttpMethod::GET,
            array(
                'config' => $config,
                'params' => $params,
            ),
            '/nat/' . $natId . '/snatRule'
        );
    }

    /**
     * Create a dnat rule in the specified dnat.
     *
     * @param string $natId
     *          The nat id of the specified dnat
     *
     * @param string $publicIpAddress
     *          The public ip address
     *
     * @param string $privateIpAddress
     *          The private ip address
     *
     * @param string $protocol
     *          The type of protocol
     *
     * @param string $publicPort
     *          The public port. The optional parameter
     *
     * @param string $privatePort
     *          The private port. The optional parameter
     *
     * @param string $ruleName
     *          The name of the dnat rule. The optional parameter
     *
     * @param string $clientToken
     *          If the clientToken is not specified by the user, a random string
     *          generated by default algorithm will be used
     *
     * @param array $options
     *          The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating NatClient instance
     *
     * @return mixed
     */
    public function createDnatRule($natId,  $publicIpAddress, $privateIpAddress, $protocol, $publicPort = null, $privatePort = null, $ruleName = null, $clientToken = null, $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');
        $body = array();
        $params = array();
        if(empty($natId)) {
            throw new \InvalidArgumentException(
                'The request $natId should not be empty.'
            );
        }
        if(empty($publicIpAddress)) {
            throw new \InvalidArgumentException(
                'The request $publicIpAddress should not be empty.'
            );
        }
        if(empty($privateIpAddress)) {
            throw new \InvalidArgumentException(
                'The request $privateIpAddress should not be empty.'
            );
        }
        if(empty($protocol)) {
            throw new \InvalidArgumentException(
                'The request $protocol should not be empty.'
            );
        }
        if (!empty($ruleName)){
            $body['ruleName'] = $ruleName;
        }
        $body['publicIpAddress'] = $publicIpAddress;
        $body['privateIpAddress'] = $privateIpAddress;
        $body['protocol'] = $protocol;
        if (!empty($publicPort)){
            $body['publicPort'] = $publicPort;
        }
        if (!empty($privatePort)){
            $body['privatePort'] = $privatePort;
        }
        if (empty($clientToken)) {
            $clientToken = $this->generateClientToken();
        }
        $params['clientToken'] = $clientToken;
        return $this->sendRequest(
            HttpMethod::POST,
            array(
                'config' => $config,
                'params' => $params,
                'body' => json_encode($body),
            ),
            '/nat/' . $natId . '/dnatRule'
        );
    }

    /**
     * Delete a dnat rule via the dnat rule id in the specified dnat.
     *
     * @param string $natId
     *          The nat id of the specified dnat
     *
     * @param string $ruleId
     *          The id of the dnat rule to be deleted
     *
     * @param string $clientToken
     *          If the clientToken is not specified by the user, a random string
     *          generated by default algorithm will be used
     *
     * @param array $options
     *          The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating NatClient instance
     *
     * @return mixed
     */
    public function deleteDnatRule($natId, $ruleId, $clientToken = null, $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');
        $params = array();
        if(empty($natId)) {
            throw new \InvalidArgumentException(
                'The request $natId should not be empty.'
            );
        }
        if(empty($ruleId)) {
            throw new \InvalidArgumentException(
                'The request $ruleId should not be empty.'
            );
        }
        if (empty($clientToken)) {
            $clientToken = $this->generateClientToken();
        }
        $params['clientToken'] = $clientToken;
        return $this->sendRequest(
            HttpMethod::DELETE,
            array(
                'config' => $config,
                'params' => $params,
            ),
            '/nat/' . $natId . '/dnatRule/' . $ruleId
        );
    }

    /**
     * Update a dnat rule via the dnat rule id in the specified dnat.
     *
     * @param string $natId
     *          The nat id of the specified dnat
     *
     * @param string $ruleId
     *          The id of the dnat rule to be updated
     *
     * @param string $ruleName
     *          The name of the dnat rule. The optional parameter
     *
     * @param string $protocol
     *          The type of protocol. The optional parameter
     *
     * @param int $publicPort
     *          The public port. The optional parameter
     *
     * @param int $privatePort
     *          The private port. The optional parameter
     *
     * @param string $privateIpAddress
     *          The private ip address. The optional parameter
     *
     * @param string $publicIpAddress
     *          The public ip address. The optional parameter
     *
     * @param string $clientToken
     *          If the clientToken is not specified by the user, a random string
     *          generated by default algorithm will be used
     *
     * @param array $options
     *          The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating NatClient instance
     *
     * @return mixed
     */
    public function updateDnatRule($natId, $ruleId, $ruleName = null, $protocol = null, $publicPort = null, $privatePort = null, $privateIpAddress = null, $publicIpAddress = null, $clientToken = null, $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');
        $body = array();
        $params = array();
        if(empty($natId)) {
            throw new \InvalidArgumentException(
                'The request $natId should not be empty.'
            );
        }
        if(empty($ruleId)) {
            throw new \InvalidArgumentException(
                'The request $ruleId should not be empty.'
            );
        }
        if (!empty($ruleName)) {
            $body['ruleName'] = $ruleName;
        }
        if (!empty($protocol)) {
            $body['protocol'] = $protocol;
        }
        if (!empty($publicPort)) {
            $body['publicPort'] = $publicPort;
        }
        if (!empty($privatePort)) {
            $body['privatePort'] = $privatePort;
        }
        if (!empty($privateIpAddress)) {
            $body['privateIpAddress'] = $privateIpAddress;
        }
        if (!empty($publicIpAddress)) {
            $body['publicIpAddress'] = $publicIpAddress;
        }
        if (empty($clientToken)) {
            $clientToken = $this->generateClientToken();
        }
        $params['clientToken'] = $clientToken;
        return $this->sendRequest(
            HttpMethod::PUT,
            array(
                'config' => $config,
                'params' => $params,
                'body' => json_encode($body),
            ),
            '/nat/' . $natId . '/dnatRule/' . $ruleId
        );
    }

    /**
     * List the information of dnat rules in one specified dnat.
     *
     * @param string $natId
     *          The nat id of the specified dnat
     *
     * @param string $marker
     *          The optional parameter marker specified in the original request to specify
     *          where in the results to begin listing.
     *
     * @param int $maxKeys
     *          The optional parameter to specifies the max number of list result to return.
     *          The default value is 1000.
     *
     * @param array $options
     *           The optional bce configuration, which will overwrite the
     *          default configuration that was passed while creating EipClient instance.
     *
     * @return mixed
     */
    public function listDnatRules($natId, $marker = null, $maxKeys = 1000, $options = array())
    {
        list($config) = $this->parseOptions($options, 'config');
        $params = array();
        if(empty($natId)) {
            throw new \InvalidArgumentException(
                'The request $natId should not be empty.'
            );
        }
        if (!empty($marker)) {
            $params['marker'] = $marker;
        }
        if (!empty($maxKeys)) {
            $params['maxKeys'] = $maxKeys;
        }
        return $this->sendRequest(
            HttpMethod::GET,
            array(
                'config' => $config,
                'params' => $params,
            ),
            '/nat/' . $natId . '/dnatRule'
        );
    }

}